//--------------------------------------------------------------------------------------
// Global variables
//--------------------------------------------------------------------------------------

#define FLT_MAX 3.402823466e+38F
#define mizCameraZPlane 0.1f

cbuffer perFrameBuf: register(b0)
{
	matrix g_mWorldView; // World * View
	matrix g_mProj; // Projection matrix
	
	float minVoxelSize; //should be property of prism
	float distC;
	float2 mem;
};

struct Vertex
{
	float4 pos;
	float3 tex;
};

struct Slice
{
	int prismInd;
	float zPlane;
};

struct TriData
{
	uint triInd;
	float depth;
};

StructuredBuffer<Vertex> g_sbVertexesRO : register(t0);
AppendStructuredBuffer<Slice> g_sbSlicesRW : register(u0);
RWStructuredBuffer<Vertex> g_sbViewVertexesRW : register(u1);

StructuredBuffer<Vertex> g_sbViewVertexesRO : register(t0);
StructuredBuffer<Slice> g_sbSlicesRO : register(t1);
RWStructuredBuffer<Vertex> g_sbTrianglesRW : register(u0);
RWStructuredBuffer<TriData> g_sbTriData: register(u1);

[numthreads(NUM_THREADS, 1, 1)]
void CalcViewVirtexesAnSlices(uint3 DTid : SV_DispatchThreadID)
{
	uint prismInd = DTid.x;
	float maxZ = -FLT_MAX, minZ = FLT_MAX;
	for (int i = 0; i<6; ++i)
	{
		Vertex v = g_sbVertexesRO[6*prismInd + i];
		v.pos = mul(v.pos, g_mWorldView);
		
		maxZ = max(v.pos.z, maxZ);
		minZ = min(v.pos.z, minZ);
		
		g_sbViewVertexesRW[6*prismInd + i] = v;
	}
	
	if (maxZ < mizCameraZPlane)
		return;
	
	Slice tmpSlice;
	tmpSlice.prismInd = prismInd;
	tmpSlice.zPlane = minZ + max(minVoxelSize, minZ*distC);
	while(tmpSlice.zPlane < maxZ)
	{
		if (tmpSlice.zPlane > mizCameraZPlane)
			g_sbSlicesRW.Append(tmpSlice);
		tmpSlice.zPlane += max(minVoxelSize, tmpSlice.zPlane*distC);
	} 
}

Vertex intersect(Vertex p1, Vertex p2, float zPlane)
{
    float s = (zPlane - p1.pos.z) / (p2.pos.z - p1.pos.z);
    
	Vertex res;
	res.pos = lerp(p1.pos, p2.pos, s);
	res.tex = lerp(p1.tex, p2.tex, s);
	
	res.pos = mul(res.pos, g_mProj);
	return res;
}

inline void emitTriangle(Vertex p1, Vertex p2, Vertex p3)
{
	uint triIndex = g_sbTrianglesRW.IncrementCounter();
	
	g_sbTrianglesRW[3*triIndex] = p1;
	g_sbTrianglesRW[3*triIndex + 1] = p2;
	g_sbTrianglesRW[3*triIndex + 2] = p3;

	TriData tmp = {triIndex, p1.pos.z};
	g_sbTriData[triIndex] = tmp;
}

[numthreads(NUM_THREADS, 1, 1)]
void CaclGeometry(uint3 DTid : SV_DispatchThreadID)
{		
	Slice slice = g_sbSlicesRO[DTid.x];
	float fZPlane = slice.zPlane;
	unsigned int i = 0, k, kr, kl;
    
	Vertex prism[6];
	for (k=0; k<6; ++k)
		prism[k] = g_sbViewVertexesRO[6*slice.prismInd + k];
		
    float3 s = float3( (fZPlane - prism[0].pos.z) / (prism[3].pos.z - prism[0].pos.z),
		(fZPlane - prism[1].pos.z) / (prism[4].pos.z - prism[1].pos.z),
		(fZPlane - prism[2].pos.z) / (prism[5].pos.z - prism[2].pos.z));
	
    if ((s[0] < 0.f && s[1] < 0.f && s[2] < 0.f) || (s[0] > 1.f && s[1] > 1.f && s[2] > 1.f))
		return;
	
	Vertex p[5] = {{{0.f,0.f,0.f,0.f}, {0.f,0.f,0.f}},
		{{0.f,0.f,0.f,0.f}, {0.f,0.f,0.f}},
		{{0.f,0.f,0.f,0.f}, {0.f,0.f,0.f}},
		{{0.f,0.f,0.f,0.f}, {0.f,0.f,0.f}},
		{{0.f,0.f,0.f,0.f}, {0.f,0.f,0.f}}};

	for (k = 0; k < 3; ++k)
	{
		kr = (k+1) % 3; 
		kl = (k+2) % 3;
		if (s[k] >= 0.f && s[k] <= 1.f)
			p[i++] = intersect(prism[k], prism[k+3], fZPlane);
		else if (s[k] < 0.f) 
		{
			if (s[kl] >= 0.f)
				p[i++] = intersect(prism[k], prism[kl], fZPlane);
			if (s[kr] >= 0.f)
				p[i++] = intersect(prism[k], prism[kr], fZPlane);
		}
		else
		{
			if (s[kl] <= 1.f) 
				p[i++] = intersect(prism[k+3], prism[kl+3], fZPlane);
			if (s[kr] <= 1.f)
				p[i++] = intersect(prism[k+3], prism[kr+3], fZPlane);
		}
	}

	emitTriangle(p[0], p[1], p[2]);
	if (i > 3)
	{
		emitTriangle(p[2], p[3], p[0]);
		if (i == 5)
			emitTriangle(p[3], p[4], p[0]);
	}
}

[numthreads(NUM_THREADS, 1, 1)]
void ZeroSortedData(uint3 DTid : SV_DispatchThreadID)
{
	g_sbTriData[DTid.x].depth = FLT_MAX;
}